#pragma once
#include "../shader.hpp"
#include <fstream>

namespace opengl {

/* Constructors */
OPENGLAPI shader::shader() : id(0) {}
OPENGLAPI shader::shader(u32 type) { id = api::glCreateShader(type); }
OPENGLAPI shader::shader(shader&& other) : id(other.id) { other.id = 0; }
OPENGLAPI shader::shader(u32 type, const char* src)
{
    id = api::glCreateShader(type);
    set_source(src);
    compile();
}
OPENGLAPI shader shader::copy_from(const shader& other)
{
    shader new_shader;
    i32 type = 0, src_size = 0;
    api::glGetShaderiv(other.id, 0x8B4F, &type);
    api::glGetShaderiv(other.id, 0x8B88, &src_size);
    if (src_size > 0 && type != 0)
    {
        new_shader.id = api::glCreateShader(type);
        auto src = new char[src_size];
        api::glGetShaderSource(other.id, src_size, nullptr, src);
        new_shader.set_source(src);
    }
    return new_shader;
}
OPENGLAPI shader shader::from_file(u32 type, const char* filename)
{
    shader new_shader;
    std::ifstream file(filename);
    if (file.is_open())
    {
        std::string src((std::istreambuf_iterator<char>(file)),
                        std::istreambuf_iterator<char>());
        file.close();
        new_shader.id = api::glCreateShader(type);
        new_shader.set_source(src.c_str());
        new_shader.compile();
    }
    return new_shader;
}

/* Memeber functions */
OPENGLAPI void shader::set_source(const char* src) const
{
    const char* sources[] = {src};
    api::glShaderSource(id, 1, sources, nullptr);
}
OPENGLAPI bool shader::compile() const
{
    api::glCompileShader(id);
    i32 status = 0;
    api::glGetShaderiv(id, 0x8B81, &status);
    return status == 1;
}
OPENGLAPI std::string shader::get_error() const
{
    i32 log_size = 0;
    api::glGetShaderiv(id, 0x8B84, &log_size);
    if (log_size <= 0) return "";

    std::string error_log(log_size + 1, '\0');
    // &error_log[0] I'm not sure if this is correct, but it seems to work
    api::glGetShaderInfoLog(id, log_size, nullptr, &error_log[0]);
    return error_log;
}

/* Operator overloads */
OPENGLAPI shader& shader::operator=(shader&& other)
{
    if (this != &other)
    {
        if (id != 0) api::glDeleteShader(id);
        id = other.id;
        other.id = 0;
    }
    return *this;
}

/* Destructor */
OPENGLAPI shader::~shader()
{
    if (id != 0)
    {
        api::glDeleteShader(id);
        id = 0;
    }
}

} // namespace opengl